﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Timers;
using UnityEngine;
using UnityEngine.Events;
using Random = UnityEngine.Random;

namespace TcpSocket
{
    /// <summary>
    /// 服务端
    /// </summary>
    public class TcpSocketServer
    {
        public TcpSocketServer(string ip, int port)
        {
            IP = ip;
            Port = port;
        }

        public TcpSocketServer() { }

        public string IP = "127.0.0.1";
        public int Port = 10010;

        string hertStr = "Server Information";

        Dictionary<Socket, ClientInfo> SocketInfo = new Dictionary<Socket, ClientInfo>();

        Socket server;

        public UnityAction<ClientInfo> OnConnection = null;
        public UnityAction<ClientInfo, string> OnReceive = null;
        public UnityAction<ClientInfo> OnClientQuit = null;
        public UnityAction OnDisconnected = null;
        System.Timers.Timer timer = null;
        public void Initialized()
        {
            if (timer == null)
            {
                timer = new System.Timers.Timer(1000);
                timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            }
        }

        /// <summary>
        /// 群发消息
        /// </summary>
        public void sendAllMsg(string resMsg)
        {
            foreach (var client in SocketInfo)
            {
                client.Key.Send(Encoding.UTF8.GetBytes(resMsg));
            }
        }
        /// <summary>
        /// 发送消息给客户端
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="resMsg"></param>
        public bool Send(Socket socket, string resMsg)
        {
            bool state = SocketInfo.ContainsKey(socket);
            if (state)
            {
                socket.Send(Encoding.UTF8.GetBytes(resMsg));
            }
            else
            {
                Debug.Log("客户端不存在!");
            }
            return state;
        }

        public void Close()
        {
            OnDisconnected.Invoke();
            timer.Stop();
            if (server != null) server.Close();
            foreach (var client in SocketInfo)
            {
                client.Key.Close();
            }
        }

        /// <summary>
        /// 开始启动 
        /// </summary>
        public void Connection()
        {
            timer.Start();
            //实例化服务端socket对象
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(IP);
            IPEndPoint point = new IPEndPoint(ip, Port);//封装保存IP和端口号

            server.Bind(point);//把IP和端口号保存到服务端socket对象
            server.Listen(10);//监听服务端
            Debug.Log("服务器启动。。。");
            server.NoDelay = true;
            server.BeginAccept(new AsyncCallback(AcceptClient), server);
        }

        void AcceptClient(IAsyncResult ar)
        {
            Socket myserver = ar.AsyncState as Socket;
            Socket client = myserver.EndAccept(ar);//保存异步连接的客户端
            /////////
            string idName = RandomOperate.GetRandomCode(15);
            ClientInfo info = new ClientInfo();
            info.socket = client;
            info.id = idName;
            info.date = DateTime.Now;
            SocketInfo.Add(client, info);
            OnConnection.Invoke(info);
            
            ////////
            Thread th = new Thread(ReceiveMsg);
            th.Start(client);
            myserver.BeginAccept(new AsyncCallback(AcceptClient), myserver);

            //Debug.Log("有新客户端连接。。。");
            //Debug.Log("客户ip信息:" + client.RemoteEndPoint);
            //string msg = "系统信息：欢迎连接!";
            //byte[] data = Encoding.UTF8.GetBytes(msg);
            //client.Send(data);//给客户端发送信息
        }

        //接收信息
        void ReceiveMsg(object socket)
        {
            Socket mySocket = socket as Socket;
            ClientInfo info = SocketInfo[mySocket];

            while (true)
            {
                byte[] buffer = new byte[1024];
                int length = 0;
                try
                {
                    length = mySocket.Receive(buffer);
                }
                catch (Exception e)
                {
                    //_quit(info,mySocket);
                    break;
                }

                if (length > 0)
                {
                    string resMsg = Encoding.UTF8.GetString(buffer, 0, length); 
                    if (hertStr != resMsg)
                    {
                        OnReceive.Invoke(info, resMsg);
                    }
                }

            }
        }

        void _quit(ClientInfo info, Socket socket)
        {
            if (socket.Poll(1000, SelectMode.SelectRead))
            {
                OnClientQuit.Invoke(info);
                SocketInfo.Remove(socket);
            }
        }

        /// <summary>
        /// 每秒服务端向客户端推送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (SocketInfo.Count > 0)
            {
                foreach (var client in SocketInfo)
                {
                    byte[] bs = Encoding.UTF8.GetBytes(hertStr);
                    if (client.Key.Poll(1000, SelectMode.SelectRead))
                    //SelectMode.SelectRead表示，如果已调用 并且有挂起的连接，true。
                    //- 或 - 如果有数据可供读取，则为 true。- 或 - 如果连接已关闭、重置或终止，则返回 true（此种情况就表示若客户端断开连接了，则此方法就返回true）； 否则，返回 false。
                    {
                        OnClientQuit.Invoke(client.Value);
                        client.Key.Close();//关闭socket
                        SocketInfo.Remove(client.Key);//从列表中删除断开的socke
                        continue;
                    }
                    client.Key.Send(bs, bs.Length, 0);
                }
            }
        }
    }

    public class ClientInfo
    {
        public Socket socket;
        public string id;
        public DateTime date;
    }

    public class RandomOperate
    {
        // 一：随机生成不重复数字字符串 
        private static int rep = 0;
        public static string GenerateCheckCodeNum(int codeCount)
        {
            string str = string.Empty;
            long num2 = DateTime.Now.Ticks + rep;
            rep++;
            System.Random random = new System.Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
            for (int i = 0; i < codeCount; i++)
            {
                int num = random.Next();
                str = str + ((char)(0x30 + ((ushort)(num % 10)))).ToString();
            }
            return str;
        }

        //方法二：随机生成字符串（数字和字母混和）
        public static string GenerateCheckCode(int codeCount)
        {
            string str = string.Empty;
            long num2 = DateTime.Now.Ticks + rep;
            rep++;
            System.Random random = new System.Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
            for (int i = 0; i < codeCount; i++)
            {
                char ch;
                int num = random.Next();
                if ((num % 2) == 0)
                {
                    ch = (char)(0x30 + ((ushort)(num % 10)));
                }
                else
                {
                    ch = (char)(0x41 + ((ushort)(num % 0x1a)));
                }
                str = str + ch.ToString();
            }
            return str;
        }

        /// <summary>
        /// 从字符串里随机得到，规定个数的字符串.
        /// </summary>
        /// <param name="allChar"></param>
        /// <param name="CodeCount"></param>
        /// <returns></returns>
        public static string GetRandomCode(int CodeCount)
        {
            string allChar = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
            string[] allCharArray = allChar.Split(',');
            string RandomCode = "";
            int temp = -1;
            System.Random rand = new System.Random();
            for (int i = 0; i < CodeCount; i++)
            {
                if (temp != -1)
                {
                    rand = new System.Random(temp * i * ((int)DateTime.Now.Ticks));
                }
                int t = rand.Next(allCharArray.Length - 1);
                while (temp == t)
                {
                    t = rand.Next(allCharArray.Length - 1);
                }
                temp = t;
                RandomCode += allCharArray[t];
            }
            return RandomCode;
        }

    }
}